﻿using System.Security.Claims;
using Duende.IdentityModel;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Devonline.Identity;

/// <summary>
/// 统一认证与授权, 登录过程描述
/// 登录分系统账户登录和第三方账户登录
/// 账户登录方式又分原账户密码登录, 短信验证码登录
/// 第三方账户登录可能存在多种账户的情况, 比如微信开放平台和微信公众平台账户, QQ等其他第三方账户
/// 因此, 目前的系统账户设计思路是 一个系统账户(User表, 包含了用户账户和手机号码) + 多个第三方账户(OAuthUser表, 包含了指向 User 的外键以进行同一个用户的多个第三方账户的关联)
/// 此时, 存在用户只使用第三方账户登录而不注册系统账户的情况, 此时系统账户就需要自动创建, 并建立关联; 也存在系统账户已经存在, 但同样是直接使用第三方账户登录的情况, 此时就需要有一个方式在第三方账户初次登录系统时, 对系统账户进行关联, 此处借鉴国内大部分系统的设计思路, 使用用户的手机号码进行验证并关联, 这就需要在初次使用第三方账户登录时, 进行一次手机号码短信校验, 以确认当前第三方账户对应的手机号码和系统账户(不存在时, 自动创建系统账户)
/// </summary>
public class AuthenticationService(
    UserManager<User> userManager,
    SignInManager<User> signInManager,
    HttpSetting httpSetting,
    IdentityDbContext context,
    ILogger<AuthenticationService> logger,
    IDistributedCache cache,
    IDataService<User> userService,
    IDataService<OAuthUser> oauthUserService,
    IDataService<RealNameInfo> realNameService,
    IHttpContextAccessor httpContextAccessor)
{
    private readonly UserManager<User> _userManager = userManager;
    private readonly SignInManager<User> _signInManager = signInManager;
    private readonly HttpSetting _httpSetting = httpSetting;
    private readonly IdentityDbContext _context = context;
    private readonly ILogger<AuthenticationService> _logger = logger;
    private readonly IDistributedCache _cache = cache;
    private readonly IDataService<User> _userService = userService;
    private readonly IDataService<OAuthUser> _oauthUserService = oauthUserService;
    private readonly IDataService<RealNameInfo> _realNameService = realNameService;
    private readonly HttpContext _httpContext = httpContextAccessor.HttpContext!;
    private static readonly string _cacheKeyPrefix = CACHE_USER + nameof(AuthType.Captcha) + CHAR_UNDERLINE;

    /// <summary>
    /// ## 2. 使用第三方账户登录
    /// 2.1 登录第三方账户后, 如果第三方账户在系统中不存在, 则创建第三方账户记录(OAuthUser表记录)
    /// 2.2 如果系统账户已经存在, 且第三方账户已经关联到系统账户, 直接进入步骤: 2.5
    /// 2.3 登录第三方账户后, 经过查找, 第三方账户尚未关联到系统账户, 则引导重定向到手机号码短信验证码验证界面去绑定手机号码
    /// 2.4 根据手机号码查找系统账户, 如果系统账户尚不存在, 根据手机号码和第三方账户信息创建系统账户(User记录); 关联登录的第三方账户和系统账户
    /// 2.5 根据第三方账户查找出对应的系统账户, 然后构造登录信息进行登录
    /// </summary>
    public async Task<User> LoginAsync()
    {
        // read external identity from the temporary cookie
        var scheme = CookieAuthenticationDefaults.AuthenticationScheme;
        var result = await _httpContext.AuthenticateAsync(scheme);
        if (!(result?.Succeeded ?? false))
        {
            throw new Exception("External authentication error");
        }

        var principal = result.Principal ?? _httpContext.User;
        ArgumentNullException.ThrowIfNull(principal);
        // try to determine the unique id of the external user (issued by the provider)
        // the most common claim type for that are the sub claim and the NameIdentifier
        // depending on the external provider, some other claim type might be used
        var userIdClaim = principal.FindFirst(JwtClaimTypes.Subject) ?? principal.FindFirst(ClaimTypes.NameIdentifier) ?? throw new Exception("Unknown userid");

        // remove the user id claim so we don't include it as an extra claim if/when we provision the user
        List<Claim> claims = [.. principal.Claims, .. _httpContext.User.Claims];
        claims = claims.Distinct(new MemberEqualityComparer<Claim, string>(x => x.Type)).ToList();
        //claims.Remove(userIdClaim);

        var provider = result.Properties?.Items["scheme"];
        ArgumentNullException.ThrowIfNull(provider);

        var providerUserId = userIdClaim.Value;
        var openId = claims.GetClaimValue(CLAIM_TYPE_OPEN_ID, provider) ?? providerUserId;
        var name = claims.GetClaimValue(JwtClaimTypes.Name, provider) ?? claims.GetClaimValue(ClaimTypes.Name) ?? claims.GetClaimValue(CLAIM_TYPE_ALIAS);
        var nickname = claims.GetClaimValue(JwtClaimTypes.NickName, provider);
        var headImgUrl = claims.GetClaimValue(CLAIM_TYPE_HEAD_IMG_URL, provider) ?? claims.GetClaimValue(CLAIM_TYPE_HEAD_IMAGE);
        var email = claims.GetClaimValue(JwtClaimTypes.Email, provider) ?? claims.GetClaimValue(ClaimTypes.Email);
        var gender = claims.GetClaimValue(JwtClaimTypes.Gender, provider);

        ArgumentNullException.ThrowIfNull(openId);
        name ??= nickname ?? openId;
        var description = name + CHAR_AT + provider;
        var oauthUser = await _oauthUserService.GetQueryable(x => x.OpenId == openId).Include(x => x.User).FirstOrDefaultAsync();

        //2.1 登录第三方账户后, 如果第三方账户在系统中不存在, 则创建第三方账户记录(OAuthUser表记录)
        oauthUser ??= await _oauthUserService.AddAsync(new OAuthUser
        {
            Id = openId,
            Type = AuthorizeType.ThirdParty,
            Name = name,
            Alias = nickname,

            OpenId = openId,
            AuthType = GetAuthTypeByProvider(provider),

            Image = headImgUrl,
            Gender = string.IsNullOrEmpty(gender) ? Gender.Unknown : (Gender)Enum.Parse(typeof(Gender), gender),
            Description = description,
            UnionId = claims.GetClaimValue("unionid", provider),
            Privileges = claims.GetClaimValue("privilege", provider)
        });

        var redirectUri = result.Properties?.RedirectUri;
        if (oauthUser.User is not null)
        {
            //2.2 如果系统账户已经存在, 且第三方账户已经关联到系统账户, 直接进入步骤: 2.5
            //2.5 根据第三方账户查找出对应的系统账户, 然后构造登录信息进行登录
            await SignInAsync(oauthUser.User, scheme, redirectUri);
            return oauthUser.User;
        }

        var userIdentifier = claims.GetClaimValue(CLAIM_TYPE_USER_IDENTIFIER);
        var user = (await _userManager.FindByLoginAsync(provider, providerUserId)) ?? (await _userManager.FindByNameAsync(providerUserId));
        RealNameInfo? realNameInfo = null;
        if (user is null)
        {
            if (_httpSetting.SecurityLevel == SecurityLevel.Basic)
            {
                userIdentifier ??= claims.GetClaimValue(CLAIM_TYPE_PHONE_NUMBER, provider) ?? claims.GetClaimValue(JwtClaimTypes.PhoneNumber, provider);
                if (!string.IsNullOrWhiteSpace(userIdentifier))
                {
                    user = await _userService.FirstOrDefaultAsync(x => x.PhoneNumberConfirmed && x.PhoneNumber != null && x.PhoneNumber == userIdentifier);
                }
            }
            else if (_httpSetting.SecurityLevel >= SecurityLevel.Security)
            {
                userIdentifier ??= claims.GetClaimValue(nameof(IdCard).ToCamelCase(), provider);
                if (!string.IsNullOrWhiteSpace(userIdentifier))
                {
                    realNameInfo = await _realNameService.GetQueryable(x => x.AuthPhase == AuthPhase.Finish && x.IdCode != null && x.IdCode == userIdentifier).Include(x => x.User).FirstOrDefaultAsync();
                    if (realNameInfo is not null && realNameInfo.User is not null)
                    {
                        user = realNameInfo.User;
                    }
                }
            }
        }

        if (user is not null)
        {
            //2.3 登录第三方账户后, 经过查找, 第三方账户尚未关联到系统账户, 则引导重定向到手机号码短信验证码验证界面去绑定手机号码
            //2.4 根据手机号码查找系统账户, 此时, 手机号码已经过了验证, 只需要关联登录的第三方账户和系统账户
            oauthUser.UserId = user.Id;
            await _oauthUserService.UpdateAsync(oauthUser);

            //2.5 根据第三方账户查找出对应的系统账户, 然后构造登录信息进行登录
            await SignInAsync(user, scheme, redirectUri);
            return user;
        }

        if (string.IsNullOrWhiteSpace(userIdentifier))
        {
            //依然不存在手机号码
            //2.3 登录第三方账户后, 经过查找, 第三方账户尚未关联到系统账户, 则引导重定向到手机号码短信验证码验证界面去绑定手机号码
            user = new User { Id = openId, Name = name, UserName = openId, State = DataState.Draft };
            await SignInAsync(user, scheme, redirectUri);
        }
        else
        {
            //此时如果手机号码存在了
            //2.4 根据手机号码查找系统账户, 如果系统账户尚不存在, 根据手机号码和第三方账户信息创建系统账户(User记录); 关联登录的第三方账户和系统账户
            user = new User
            {
                Name = name,
                Alias = nickname,
                UserName = providerUserId,
                Email = email,
                Image = headImgUrl,
                Type = AuthorizeType.ThirdParty,
                Description = description
            };

            user.Create();
            user.Update();

            if (_httpSetting.SecurityLevel == SecurityLevel.Basic)
            {
                user.PhoneNumberConfirmed = true;
                user.PhoneNumber = userIdentifier;
            }
            else if (_httpSetting.SecurityLevel >= SecurityLevel.Security && realNameInfo is not null)
            {
                user.PhoneNumberConfirmed = true;
                user.PhoneNumber = realNameInfo.PhoneNumber;
                realNameInfo.UserId = user.Id;
                await _realNameService.UpdateAsync(realNameInfo);
                _logger.LogInformation($"已关联实名认证用户 {realNameInfo.IdCode} 到用户 {user.Name}!");
            }
            else
            {
                //这种情况下不保存用户
                user.State = DataState.Draft;
                await SignInAsync(user, scheme, redirectUri);
                return user;
            }

            var identityResult = await _userManager.CreateAsync(user);
            if (!identityResult.Succeeded)
            {
                throw new Exception(identityResult.Errors.First().Description);
            }

            _logger.LogInformation($"已按认证用户 {openId} 创建用户 {user.Name}!");

            oauthUser.UserId = user.Id;
            oauthUser = await _oauthUserService.UpdateAsync(oauthUser);
            _logger.LogInformation($"已关联认证用户 {openId} 到用户 {user.Name}!");

            if (!claims.Any(x => x.Type == CLAIM_TYPE_USER_NAME))
            {
                claims.Append(new(CLAIM_TYPE_USER_NAME, user.UserName));
            }

            identityResult = await _userManager.AddClaimsAsync(user, claims);
            if (!identityResult.Succeeded)
            {
                throw new Exception(identityResult.Errors.First().Description);
            }

            identityResult = await _userManager.AddLoginAsync(user, new UserLoginInfo(provider, providerUserId, provider));
            if (!identityResult.Succeeded)
            {
                throw new Exception(identityResult.Errors.First().Description);
            }

            _logger.LogInformation($"已添加认证用户 {openId} 到用户 {user.Name} claims 和 login info!");

            await SignInAsync(user, scheme, redirectUri);
        }

        return user;
    }

    /// <summary>
    /// 微信登录
    /// </summary>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public async Task<User> WeixinLoginAsync()
    {
        // read external identity from the temporary cookie
        var scheme = CookieAuthenticationDefaults.AuthenticationScheme;
        var principal = _httpContext.User;
        var userIdClaim = principal.FindFirst(JwtClaimTypes.Subject) ?? principal.FindFirst(ClaimTypes.NameIdentifier) ?? throw new Exception("Unknown userid");

        // remove the user id claim so we don't include it as an extra claim if/when we provision the user
        var claims = _httpContext.User.Claims;
        claims = claims.Distinct(new MemberEqualityComparer<Claim, string>(x => x.Type)).ToList();

        var provider = "Weixin";
        var providerUserId = userIdClaim.Value;
        var openId = claims.GetClaimValue(CLAIM_TYPE_OPEN_ID, provider) ?? providerUserId;
        var name = claims.GetClaimValue(JwtClaimTypes.Name, provider) ?? claims.GetClaimValue(ClaimTypes.Name);
        var nickname = claims.GetClaimValue(JwtClaimTypes.NickName, provider) ?? claims.GetClaimValue(CLAIM_TYPE_ALIAS);
        var headImgUrl = claims.GetClaimValue(CLAIM_TYPE_HEAD_IMG_URL, provider) ?? claims.GetClaimValue(CLAIM_TYPE_HEAD_IMAGE);
        var email = claims.GetClaimValue(JwtClaimTypes.Email, provider) ?? claims.GetClaimValue(ClaimTypes.Email);
        var gender = claims.GetClaimValue(JwtClaimTypes.Gender, provider);

        ArgumentNullException.ThrowIfNull(openId);
        name ??= nickname ?? openId;
        var description = name + CHAR_AT + provider;
        var oauthUser = await _oauthUserService.GetQueryable(x => x.OpenId == openId).Include(x => x.User).AsNoTracking().FirstOrDefaultAsync();

        //2.1 登录第三方账户后, 如果第三方账户在系统中不存在, 则创建第三方账户记录(OAuthUser表记录)
        oauthUser ??= await _oauthUserService.AddAsync(new OAuthUser
        {
            Id = openId,
            Type = AuthorizeType.ThirdParty,
            Name = name,
            Alias = nickname,

            OpenId = openId,
            AuthType = GetAuthTypeByProvider(provider),

            Image = headImgUrl,
            Gender = string.IsNullOrEmpty(gender) ? Gender.Unknown : (Gender)Enum.Parse(typeof(Gender), gender),
            Description = description,
            UnionId = claims.GetClaimValue("unionid", provider),
            Privileges = claims.GetClaimValue("privilege", provider)
        });

        if (oauthUser.User is not null)
        {
            //2.2 如果系统账户已经存在, 且第三方账户已经关联到系统账户, 直接进入步骤: 2.5
            //2.5 根据第三方账户查找出对应的系统账户, 然后构造登录信息进行登录
            await SignInAsync(oauthUser.User, scheme);
            return oauthUser.User;
        }

        var userIdentifier = claims.GetClaimValue(CLAIM_TYPE_USER_IDENTIFIER);
        var user = (await _userManager.FindByLoginAsync(provider, providerUserId)) ?? (await _userManager.FindByNameAsync(providerUserId));
        RealNameInfo? realNameInfo = null;
        if (user is null)
        {
            if (_httpSetting.SecurityLevel == SecurityLevel.Basic)
            {
                userIdentifier ??= claims.GetClaimValue(CLAIM_TYPE_PHONE_NUMBER, provider) ?? claims.GetClaimValue(JwtClaimTypes.PhoneNumber, provider);
                if (!string.IsNullOrWhiteSpace(userIdentifier))
                {
                    user = await _userService.FirstOrDefaultAsync(x => x.PhoneNumberConfirmed && x.PhoneNumber != null && x.PhoneNumber == userIdentifier);
                }
            }
            else if (_httpSetting.SecurityLevel >= SecurityLevel.Security)
            {
                userIdentifier ??= claims.GetClaimValue(nameof(IdCard).ToCamelCase(), provider);
                if (!string.IsNullOrWhiteSpace(userIdentifier))
                {
                    realNameInfo = await _realNameService.GetQueryable(x => x.AuthPhase == AuthPhase.Finish && x.IdCode != null && x.IdCode == userIdentifier).Include(x => x.User).AsNoTracking().FirstOrDefaultAsync();
                    if (realNameInfo is not null && realNameInfo.User is not null)
                    {
                        user = realNameInfo.User;
                    }
                }
            }
        }

        if (user is not null)
        {
            //2.3 登录第三方账户后, 经过查找, 第三方账户尚未关联到系统账户, 则引导重定向到手机号码短信验证码验证界面去绑定手机号码
            //2.4 根据手机号码查找系统账户, 此时, 手机号码已经过了验证, 只需要关联登录的第三方账户和系统账户
            oauthUser.UserId = user.Id;
            oauthUser.Name = user.Name;
            //oauthUser.Alias = user.Alias;
            await _oauthUserService.UpdateAsync(oauthUser);

            //2.5 根据第三方账户查找出对应的系统账户, 然后构造登录信息进行登录
            await SignInAsync(user, scheme);
            return user;
        }

        if (string.IsNullOrWhiteSpace(userIdentifier))
        {
            //依然不存在手机号码
            //2.3 登录第三方账户后, 经过查找, 第三方账户尚未关联到系统账户, 则引导重定向到手机号码短信验证码验证界面去绑定手机号码
            user = new User { Id = openId, Name = name, UserName = openId, Type = AuthorizeType.Authenticator, State = DataState.Draft };
            _logger.LogInformation($"上下文信息不足以创建用户, 将使用临时用户信息构造登录信息: user id {user.Id}, user name {name}");
            await SignInAsync(user, scheme);
        }
        else
        {
            //此时如果手机号码存在了
            //2.4 根据手机号码查找系统账户, 如果系统账户尚不存在, 根据手机号码和第三方账户信息创建系统账户(User记录); 关联登录的第三方账户和系统账户
            user = new User
            {
                Name = name,
                Alias = nickname,
                UserName = providerUserId,
                Email = email,
                Image = headImgUrl,
                Type = AuthorizeType.ThirdParty,
                Description = description
            };

            var userId = _httpContext.GetClaimValue<string>(CLAIM_TYPE_USER_ID);
            if (userId is not null)
            {
                _logger.LogInformation($"上下文已存在 userId {userId}, 将使用此值创建用户!");
                user.Id = userId;
            }

            _logger.LogInformation($"将根据上下文信息创建用户: user id {user.Id}, user name {name}");
            user.Create();
            user.Update();

            if (_httpSetting.SecurityLevel == SecurityLevel.Basic)
            {
                user.PhoneNumberConfirmed = true;
                user.PhoneNumber = userIdentifier;
            }
            else if (_httpSetting.SecurityLevel >= SecurityLevel.Security && realNameInfo is not null)
            {
                user.PhoneNumberConfirmed = true;
                user.PhoneNumber = realNameInfo.PhoneNumber;
                realNameInfo.UserId = user.Id;
                await _realNameService.UpdateAsync(realNameInfo);
                _logger.LogInformation($"已关联实名认证用户 {realNameInfo.IdCode} 到用户 {user.Name}!");
            }
            else
            {
                //这种情况下不保存用户
                user.State = DataState.Draft;
                await SignInAsync(user, scheme);
                return user;
            }

            var identityResult = await _userManager.CreateAsync(user);
            if (!identityResult.Succeeded)
            {
                throw new Exception(identityResult.Errors.First().Description);
            }

            _logger.LogInformation($"已按认证用户 {openId} 创建用户 {user.Name}!");

            oauthUser.UserId = user.Id;
            oauthUser.Name = user.Name;
            oauthUser.Alias = user.Alias;
            oauthUser = await _oauthUserService.UpdateAsync(oauthUser);
            _logger.LogInformation($"已关联认证用户 {openId} 到用户 {user.Name}!");

            if (!claims.Any(x => x.Type == CLAIM_TYPE_USER_NAME))
            {
                claims.Append(new(CLAIM_TYPE_USER_NAME, user.UserName));
            }

            var userClaims = new Claim[] { new(CLAIM_TYPE_USER_ID, user.Id), new(CLAIM_TYPE_USER_NAME, user.UserName), new(CLAIM_TYPE_NAME, user.Name) };
            identityResult = await _userManager.AddClaimsAsync(user, userClaims);
            if (!identityResult.Succeeded)
            {
                throw new Exception(identityResult.Errors.First().Description);
            }

            identityResult = await _userManager.AddLoginAsync(user, new UserLoginInfo(provider, providerUserId, provider));
            if (!identityResult.Succeeded)
            {
                throw new Exception(identityResult.Errors.First().Description);
            }

            _logger.LogInformation($"已添加认证用户 {openId} 到用户 {user.Name} claims 和 login info!");

            await SignInAsync(user, scheme);
        }

        return user;
    }

    /// <summary>
    /// 根据用户获取重定向的地址
    /// </summary>
    /// <param name="user">当前登录用户</param>
    /// <param name="redirectUrl">重定向地址</param>
    /// <returns></returns>
    public string GetRedirectUrl(User user, string? redirectUrl = default)
    {
        redirectUrl ??= _httpContext.GetClaimValue<string>(CLAIM_TYPE_REDIRECT_URL) ?? _httpContext.GetClaimValue<string>(CLAIM_TYPE_REDIRECT_URI);
        if (string.IsNullOrWhiteSpace(redirectUrl) && _httpContext.Items.TryGetValue(nameof(AuthenticationProperties.RedirectUri), out object? value) && value is not null)
        {
            redirectUrl = value.ToString();
        }

        redirectUrl ??= CHAR_SLASH.ToString();
        if (string.IsNullOrWhiteSpace(user.PhoneNumber) || !user.PhoneNumberConfirmed)
        {
            redirectUrl = _httpSetting.UserInteraction!.BindPhoneNumber + $"?{CLAIM_TYPE_REDIRECT_URL}={redirectUrl}";
        }

        return redirectUrl;
    }
    /// <summary>
    /// 增加用户自定义 claims
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    public async Task<List<Claim>> GetUserClaimsAsync(User user) => await user.CopyTo<UserViewModel>().GetUserClaimsAsync(_context, _httpSetting.DataIsolate);

    /// <summary>
    /// if the external login is OIDC-based, there are certain things we need to preserve to make logout work, this will be different for WS-Fed, SAML2p or other protocols
    /// </summary>
    /// <param name="result"></param>
    /// <returns></returns>
    public static AuthenticationProperties ProcessLoginCallback(AuthenticateResult result)
    {
        // this allows us to collect any additional claims or properties
        // for the specific protocols used and store them in the local auth cookie.
        // this is typically used to store data needed for signout from those protocols.

        // if the external provider issued an id_token, we'll keep it for signout
        var properties = new AuthenticationProperties();
        var idToken = result.Properties?.GetTokenValue("id_token");
        if (idToken is not null)
        {
            properties.StoreTokens([new AuthenticationToken { Name = "id_token", Value = idToken }]);
        }

        return properties;
    }

    /// <summary>
    /// 获取 cookie 类型的 scheme
    /// </summary>
    /// <returns></returns>
    /// <exception cref="InvalidOperationException"></exception>
    private async Task<string> GetCookieAuthenticationSchemeAsync()
    {
        var schemes = _httpContext.RequestServices.GetRequiredService<IAuthenticationSchemeProvider>();
        var scheme = await schemes.GetDefaultAuthenticateSchemeAsync() ?? throw new InvalidOperationException("No DefaultAuthenticateScheme found or no CookieAuthenticationScheme configured on IdentityServerOptions.");
        return scheme.Name;
    }
    /// <summary>
    /// 根据 provider 获取认证方式
    /// </summary>
    /// <param name="provider"></param>
    /// <returns></returns>
    private AuthType GetAuthTypeByProvider(string provider)
    {
        try
        {
            return Enum.Parse<AuthType>(provider);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "根据 provider 获取认证方式失败, 将按 OAuth2 作为默认情况处理!");
        }

        return AuthType.OAuth2;
    }
    /// <summary>
    /// 用户登录
    /// </summary>
    /// <param name="user"></param>
    /// <param name="scheme"></param>
    /// <param name="redirectUri"></param>
    /// <returns></returns>
    private async Task SignInAsync(User user, string scheme, string? redirectUri = default)
    {
        _logger.LogDebug("user {user} will sign in with claims!", user.UserName);
        user.PasswordHash = null;
        var claims = await GetUserClaimsAsync(user);
        if ((user.PhoneNumberConfirmed && !string.IsNullOrWhiteSpace(user.PhoneNumber)))
        {
            user.PhoneNumber = user.PhoneNumber.Desensitize();
            _logger.LogInformation("user {user} has confirmed the phoneNumber, and will sign in with user claims!", user.UserName);
        }
        else
        {
            claims.Add(new Claim(CLAIM_TYPE_JWT_SUBJECT, user.Id));
            claims.Add(new Claim(CLAIM_TYPE_OPEN_ID, user.UserName!));
            _logger.LogInformation("user {user} has not confirmed the phoneNumber, and will sign in without user claims!", user.UserName);
        }

        _signInManager.AuthenticationScheme = scheme;
        await _signInManager.SignOutAsync();
        await _signInManager.SignInWithClaimsAsync(user, true, claims);
        _logger.LogInformation($"user {user.UserName} login from Identity Service success! ");
    }
}